#!/usr/bin/env python3
import os
import json
from datetime import datetime, timedelta, date
import socket
import requests
from collections import defaultdict
import math

# 配置
DEEPSEEK_API_KEY = "sk-***" #输入你的API KEY
DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
RASPBERRY_PI_IP = "192.168.*.*" #改成你的树莓派地址
RASPBERRY_PI_PORT = 5000
DATA_DIR = os.path.expanduser("~/schedule_data")
os.makedirs(DATA_DIR, exist_ok=True)

class SM2Scheduler:
    """SM-2间隔重复算法实现"""
    def __init__(self):
        self.review_db = os.path.join(DATA_DIR, "review_db.json")
        self.load_data()

    def load_data(self):
        try:
            with open(self.review_db, 'r') as f:
                self.data = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            self.data = {"items": {}, "history": {}}

    def save_data(self):
        with open(self.review_db, 'w') as f:
            json.dump(self.data, f, indent=2)

    def add_task(self, task, tags=None):
        item_id = str(len(self.data["items"]) + 1)
        self.data["items"][item_id] = {
            "task": task,
            "efactor": 2.5,
            "interval": 0,
            "repetition": 0,
            "tags": tags or [],
            "created": date.today().isoformat()
        }
        self.save_data()
        return item_id

    def get_due_reviews(self):
        due_items = []
        today = date.today()
        
        for item_id, item in self.data["items"].items():
            if item["interval"] > 0:
                last_review = max(
                    [datetime.strptime(d, "%Y-%m-%d").date() 
                    for d in self.data["history"].get(item_id, [])],
                    default=None
                )
                if last_review:
                    next_review = last_review + timedelta(days=item["interval"])
                    if today >= next_review:
                        due_items.append((item_id, item["task"]))
                else:
                    due_items.append((item_id, item["task"]))
        return due_items

    def log_review(self, item_id, quality):
        today = date.today().isoformat()
        
        if item_id not in self.data["history"]:
            self.data["history"][item_id] = []
        self.data["history"][item_id].append(today)
        
        item = self.data["items"][item_id]
        if quality >= 3:
            if item["repetition"] == 0:
                item["interval"] = 1
            elif item["repetition"] == 1:
                item["interval"] = 3
            else:
                item["interval"] = math.ceil(item["interval"] * item["efactor"])
            item["repetition"] += 1
        else:
            item["repetition"] = 0
            item["interval"] = 1
        
        item["efactor"] = max(1.3, item["efactor"] + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)))
        self.save_data()

class DailyScheduler:
    def __init__(self):
        self.sm2 = SM2Scheduler()
        os.makedirs(DATA_DIR, exist_ok=True)

    def get_today_file(self):
        return os.path.join(DATA_DIR, f"schedule_{date.today().isoformat()}.json")

    def generate_schedule(self, new_tasks):
        """生成包含复习任务的时间表"""
        reviews = self.sm2.get_due_reviews()
        
        # 构建AI提示词
        prompt = f"""请为以下任务生成今日({date.today()})时间表：
要求：一下要求需要严格按照。所有时间段的任务都要安排并返回，即使是固定时间如吃饭和锻炼等也要加入安排并返回。
1. 返回严格JSON格式：[{{"time":"HH:MM-HH:MM","task":"内容","type":"new/review"}}]，去掉所有不必要的标记
2. 工作时间06:00-22:00
3. 任务时间20分钟到60分钟不等，按照具体执行情况调整。比如考试就多安排点时间，背诵就控制在30分钟以内。没有休息时间
4. 包含午餐休息12:00-14:00
5. 所有时间就算没安排也要返回
6. 上午9点和下午5点开始锻炼20分钟
7. 吃饭时间早上7点开始，中午12点开始，下午6点开始，每次半小时
8. 早读安排背诵任务，中午和晚上饭前以及睡前要安排10分钟的复习。
9. 以***开头的任务当作指令执行，不计入任务。
10. 所有时间段的任务都要安排并返回，即使是固定时间如吃饭和锻炼等也要加入安排并返回。

新任务：
{json.dumps(new_tasks, indent=2, ensure_ascii=False)}

待复习：
{json.dumps([t[1] for t in reviews], indent=2, ensure_ascii=False)}"""

        try:
            response = requests.post(
                DEEPSEEK_API_URL,
                headers={"Authorization": f"Bearer {DEEPSEEK_API_KEY}"},
                json={
                    "model": "deepseek-chat",
                    "messages": [{"role": "user", "content": prompt}],
                    "temperature": 0.3
                },
                # timeout=10
            )
            
            response.raise_for_status()
            response_data = response.json()
            
            # 提取并清理API返回的JSON内容
            content = response_data["choices"][0]["message"]["content"]
            if content.startswith("```json"):
                # 去除Markdown代码块标记
                content = content[7:-3].strip()  # 去掉开头的```json和结尾的```
            
            schedule = json.loads(content)
            
        except requests.exceptions.RequestException as e:
            print(f"API请求失败: {str(e)}")
            if hasattr(e, 'response') and e.response:
                print(f"响应状态码: {e.response.status_code}")
                print(f"响应内容: {e.response.text[:200]}...")
            return []
        except json.JSONDecodeError as e:
            print(f"JSON解析失败: {str(e)}")
            print(f"尝试解析的内容: {content[:200]}...")
            return []
        except KeyError as e:
            print(f"API响应格式错误，缺少关键字段: {str(e)}")
            print(f"完整响应: {response_data}")
            return []
        except Exception as e:
            print(f"未知错误: {str(e)}")
            return []

        # 标记复习任务
        for item in schedule:
            if "[复习]" in item["task"]:
                item["type"] = "review"
                for rid, rtask in reviews:
                    if rtask in item["task"]:
                        item["item_id"] = rid
            else:
                item_id = self.sm2.add_task(item["task"])
                item["item_id"] = item_id
                item["type"] = "new"
        
        self._save_schedule(schedule)
        return schedule

    # 移除 _create_default_schedule 方法
    def _save_schedule(self, schedule):
        """保存日程到文件"""
        with open(self.get_today_file(), 'w') as f:
            json.dump({
                "date": date.today().isoformat(),
                "schedule": schedule,
                "generated_at": datetime.now().isoformat()
            }, f, indent=2)

    def send_to_pi(self, schedule):
        """发送到树莓派"""
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(10)  # 设置超时
            s.connect((RASPBERRY_PI_IP, RASPBERRY_PI_PORT))
            
            # 序列化JSON并添加结束标记
            json_data = json.dumps(schedule, ensure_ascii=False)
            data = (json_data + "\nEND_OF_JSON\n").encode('utf-8')  # 添加明确的结束标记
            
            # 分块发送
            chunk_size = 1024
            total_sent = 0
            while total_sent < len(data):
                sent = s.send(data[total_sent:total_sent+chunk_size])
                if sent == 0:
                    raise RuntimeError("Socket连接中断")
                total_sent += sent
            # 确保JSON数据是ASCII编码且正确处理Unicode字符
            json_data = json.dumps(schedule, ensure_ascii=False)
            
            # 添加数据长度前缀
            data_length = len(json_data)
            header = f"{data_length:08d}"  # 8位数字表示数据长度
            full_data = (header + json_data).encode('utf-8')
            
            # 分块发送数据
            chunk_size = 1024
            for i in range(0, len(full_data), chunk_size):
                s.sendall(full_data[i:i+chunk_size])

    def run(self):
        """交互式运行"""
        print(f"\n=== 每日任务生成器 ({date.today()}) ===")
        print("输入今日任务（每行一个，空行结束）：")
        
        tasks = []
        while True:
            task = input("> ").strip()
            if not task:
                if not tasks:
                    print("必须至少输入一个任务！")
                    continue
                break
            tasks.append(task)
        
        schedule = self.generate_schedule(tasks)
        
        try:
            self.send_to_pi(schedule)
            print("\n✓ 已发送到树莓派")
        except Exception as e:
            print(f"\n! 发送失败: {e}")
        
        print("\n今日安排：")
        for item in schedule:
            print(f"{item['time']} {item['task']}")

if __name__ == "__main__":
    scheduler = DailyScheduler()
    scheduler.run()